package abstractions;

import runners.*;
import runners.Runner.Gender;
import accounts.*;
import lists.*;
import graphs.*;
import tester.*;
import tester.cobertura.Instrument;

/**
 * Javadoc:
 * http://www.ccs.neu.edu/home/ada2358/Doc/Abstractions/
 * Test Class for the Lists and Graphs Packages
 * @author Adam
 * @version 1.3
 */
@Instrument
@Example
public class Examples {

	// The default constructor.
	public Examples() {}
	
	// Constants used in filter predicates:
	private static final int maxAge = 40; // Max age desired.
	private static final int time = 180; // Faster than time...
	private static final int lowBalance = 200; // The low balance.
	private static final String owner = "Bob"; // The owner desired.
	private static final String type = "class accounts.Checking"; 
		// This works... but not well...

    //=================================================================
	// Even though I can write these predicates in line as I pass them 
	// into the filter these predefined ones make life easier.
    //=================================================================

	// Runner Predicates and Comparators.
	// underMaxAge is an anonymous Class overriding UnaryFunction, 
	// and overriding the f method.  This is the age predicate.
	private static UnaryFunction<Boolean, Runner> underMaxAge = 
		new UnaryFunction<Boolean, Runner>() {
		public Boolean f(Runner r) {
			return r.getAge() <= maxAge;}};

	// fasterThan is an anonymous Class overriding UnaryFunction, 
	// and overriding the f method. This is the totalTime predicate.
	private static UnaryFunction<Boolean, Runner> fasterThan = 
		new UnaryFunction<Boolean, Runner>() {
		public Boolean f(Runner r) {
			return r.totalTime() <= time;}};
	
	// Account Predicates and Comparators.
	// typeIs is an anonymous Class overriding UnaryFunction, 
	// and overriding the f method. This is the classType predicate.
	private static UnaryFunction<Boolean, Account> typeIs = 
		new UnaryFunction<Boolean, Account>() {
		public Boolean f(Account a) {
			return a.getClassType().equals(type);}};
	
	// balanceUnder is an anonymous Class overriding UnaryFunction, 
	// and overriding the f method. This is the balance predicate.
	private static UnaryFunction<Boolean, Account> balanceUnder = 
		new UnaryFunction<Boolean, Account>() {
		public Boolean f(Account a) {
			return a.getBalance() <= lowBalance;}};

	// balanceUnder is an anonymous Class overriding UnaryFunction, 
	// and overriding the f method. This is the balance predicate.
	private static BinaryFunction<Boolean, Account, Account> lowerBalance = 
		new BinaryFunction<Boolean, Account, Account>() {
		public Boolean f(Account f, Account s) {
			return f.getBalance() < s.getBalance();
		}};

	// balanceUnder is an anonymous Class overriding UnaryFunction, 
	// and overriding the f method. This is the balance predicate.
	private static BinaryFunction<Boolean, Account, Account> higherBalance = 
	  new BinaryFunction<Boolean, Account, Account>() {
		public Boolean f(Account f, Account s) {
			return f.getBalance() > s.getBalance();
		}};

	// Examples:
	// Initialize the checking accounts.
	private Account check1 = 
	  new Checking(0001, "First Checking Account", 150, "Joe", 0);
	private Account check2 = 
	  new Checking(0002, "Second Checking Account", 100, "Bob", 50);
	private Account check3 = 
	  new Checking(0003, "Third Checking Account", 500, "Jim", 0);
	// Initialize the savings accounts.
	private Account savings1 = 
	  new Savings(0004, "First Savings Account", 0, "Jim", 4.9);
	private Account savings2 = 
	  new Savings(0005, "Second Savings Account", 100, "Bob", 8.9);
	private Account savings3 = 
	  new Savings(0006, "Third Savings Account", 2000, "Joe", 1.1);
	// Initialize the credit accounts.
	private Account credit1 = 
	  new Credit(0004, "First Credit Account", 1000, "Adam", 1000, 4.9);
	private Account credit2 = 
	  new Credit(0005, "Second Credit Account", 100, "Joe", 200, 8.9);
	private Account credit3 = 
	  new Credit(0006, "Third Credit Account", 0, "Jim", 1000, 1.1);
	// Initialize the premium checking accounts.
	private Account pcheck1 = 
	  new PremiumChecking(0001, "First Checking Account", 900, "Bob", 0, 0);
	private Account pcheck2 = 
	  new PremiumChecking(0002, "Second Checking Account", 100, "Tim", 50, 5);
	private Account pcheck3 = 
	  new PremiumChecking(0003, "Third Checking Account", 500, "Viera", 0, 25);
	
	// Initialize the Runners
	private static Runner r1 = new Runner("bob", Gender.MALE, 42, 314, 0, 628);
	private static Runner r2 = new Runner("jim", Gender.MALE, 33, 333, 0, 444);
	private static Runner r3 = new Runner("joe", Gender.MALE, 16, 66, 0, 166);
	
	// Lists of Stings:
	private static List<String> mtlos = new MTList<String>();
	private static List<String> los1 = new ConsList<String>("Foo",
			new ConsList<String>("Bar", mtlos));
	private static List<String> los2 = new ConsList<String>("Goo", los1);
	
	// Lists of Integers:
	private static List<Integer> mtloi = new MTList<Integer>();
	private static List<Integer> loi1 = new ConsList<Integer>(1,
			new ConsList<Integer>(4, mtloi));
	private static List<Integer> loi2 = new ConsList<Integer>(3, loi1);
	
	// Lists of Runners:
	private static List<Runner> mtlor = new MTList<Runner>();
	private static List<Runner> lor1 = new ConsList<Runner>(r3,
			new ConsList<Runner>(r2, mtlor));
	private static List<Runner> lor2 = new ConsList<Runner>(r1, lor1);
	
	// Lists of Accounts:
	private List<Account> mtloa = new MTList<Account>();
	private List<Account> loa1 = new ConsList<Account>(check1,
			new ConsList<Account>(credit2, mtloa));
	private List<Account> loa2 = new ConsList<Account>(pcheck1, loa1);

	private List<Account> loa3 = new ConsList<Account>(pcheck1,
			new ConsList<Account>(savings3, mtloa));
	private List<Account> loa4 = new ConsList<Account>(pcheck1,
			new ConsList<Account>(check1, mtloa));
	private List<Account> loa5 = new ConsList<Account>(check1, 
			new ConsList<Account>(pcheck1,
					new ConsList<Account>(savings3, mtloa)));
	private List<Account> loa6 = new ConsList<Account>(savings3, loa4);
	
//	/**
//	 * Main method
//	 * @param args
//	 */
//	public static void main(String[] args) {
//		final Examples e = new Examples();
//		final Tester t = new Tester();
//		// Test Methods
//		e.testOrMap(t);
//		e.testMap(t);
//		e.testFilter(t);
//		e.testSize(t);
//		e.testCount(t);
//		e.testContains(t);
//		e.testInsert(t);
//		e.testSort(t);
//	}

	//=================================================================
	// Tests On Lists.
	//=================================================================
	/**
	 * OrMap
	 * @param t
	 */
	@TestMethod
	void testOrMap(Tester t) {
		t.checkExpect(loi2.ormap(new UnaryFunction<Boolean, Integer>() {
			public Boolean f(Integer i) {
				return i < 5;
			}
		}), true);
		t.checkExpect(los2.ormap(new UnaryFunction<Boolean, String>() {
			public Boolean f(String s) {
				return s.length() < 5;
			}
		}), true);
		t.checkExpect(loa2.ormap(new UnaryFunction<Boolean, Account>() {
			public Boolean f(Account a) {
				return a.getBalance() < 200;
			}
		}), true);
	}
	
	/**
	 * Map
	 * @param t
	 */
	@TestMethod
	void testMap(Tester t) {
		t.checkExpect(loi2.map(new UnaryFunction<String, Integer>() {
			public String f(Integer i) {
				return Integer.toString(i);
			}
		}), new ConsList<String>("3",
				new ConsList<String>("1", 
						new ConsList<String>("4", 
								new MTList<String>()))));
	}

	/**
	 * Filter
	 * @param t
	 */
	@TestMethod
	void testFilter(Tester t) {
		t.checkExpect(lor2.filter(underMaxAge), lor1);
		t.checkExpect(lor1.filter(new UnaryFunction<Boolean, Runner>() {
			public Boolean f(Runner r) {
				return r.getAge() <= maxAge;
			}
		}), lor1);
		t.checkFail(lor2.filter(new UnaryFunction<Boolean, Runner>() {
			public Boolean f(Runner r) {
				return r.getAge() <= maxAge;
			}
		}), mtlor);
		t.checkExpect(loa2.filter(typeIs), new ConsList<Account>(check1, mtloa));
	}
	
	/**
	 * Size
	 * @param t
	 */
	@TestMethod
	void testSize(Tester t) {
		t.checkExpect(los1.size(), 2);
		t.checkExpect(mtlos.size(), 0);
		t.checkExpect(los2.size(), 3);
	}
	
	/**
	 * Count
	 * @param t
	 */
	@TestMethod
	void testCount(Tester t) {
		t.checkExpect(lor2.count(underMaxAge), 2);
		t.checkExpect(mtlor.count(new UnaryFunction<Boolean, Runner>() {
			public Boolean f(Runner r) {
				return r.getAge() <= maxAge;
			}
		}), 0);
		t.checkFail(lor2.count(new UnaryFunction<Boolean, Runner>() {
			public Boolean f(Runner r) {
				return r.getAge() <= maxAge;
			}
		}), 9);
	}
	
	/**
	 * Contains
	 * @param t
	 */
	@TestMethod
	void testContains(Tester t) {
		t.checkExpect(loa1.contains(check1), true);
		t.checkExpect(loa2.contains(pcheck1), true);
		t.checkFail(loa2.contains(savings2));
	}
	
	@TestMethod
	void testInsert(Tester t) {
		t.checkExpect(loa1.insert(new BinaryFunction<Boolean, Account, Account>() {
			public Boolean f(Account f, Account s) {
				return f.getBalance() >= s.getBalance();	
			}
		}, pcheck1), loa2);
		t.checkExpect(loa3.insert(lowerBalance, check1), loa5);
		t.checkExpect(loa4.insert(higherBalance, savings3), loa6);
	}

	@TestMethod
	void testSort(Tester t) {
		t.checkExpect(loa2.sort(new BinaryFunction<Boolean, Account, Account>() {
			public Boolean f(Account f, Account s) {
				return f.getBalance() >= s.getBalance();	
			}
		}), loa2);
	}
	// Need More Tests.
	
	

	
	
	
    //=================================================================
    // Tests On Graphs.
    //=================================================================
}
